package com.fight.strive.sys.modules.cauth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fight.strive.sys.modules.auth.dto.StriveAuthInfo;
import com.fight.strive.sys.modules.cauth.dao.AppClientMapper;
import com.fight.strive.sys.modules.cauth.entity.AppClientEntity;
import com.fight.strive.sys.modules.cauth.service.AppClientService;
import com.fight.strive.sys.modules.common.PageR;
import com.fight.strive.sys.modules.exception.StriveException;
import com.fight.strive.sys.modules.exception.exceptions.StriveInvalidTokenException;
import com.fight.strive.sys.modules.mybatis.MyBatisPlusUtils;
import com.fight.strive.sys.modules.rbac.entity.RbacUserEntity;
import com.fight.strive.sys.modules.redis.RedisUtils;
import com.fight.strive.sys.modules.validator.StriveValid;
import com.fight.strive.sys.utils.DigestUtils;
import com.fight.strive.sys.utils.ObjectUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;

@Service
@Slf4j
public class AppClientServiceImpl
        extends ServiceImpl<AppClientMapper, AppClientEntity>
        implements AppClientService {

    private final String appKey = "sys:app_client:app_key";

    @Override
    public void saveAppClient(@StriveValid AppClientEntity entity) {
        try {
            this.saveOrUpdate(entity);
            entity = this.getById(entity);
            this.updateCache(entity);
        } catch (DuplicateKeyException e) {
            throw new StriveException("appKey 已经存在");
        }
    }

    @Override
    public IPage<AppClientEntity> listByPage(PageR<AppClientEntity> page) {
        QueryWrapper<AppClientEntity> queryWrapper = new QueryWrapper<>();
        MyBatisPlusUtils.buildAndQuery(queryWrapper, page.getCond());
        return this.page(page, queryWrapper);
    }

    @Override
    public void removeAppClient(AppClientEntity entity) {
        entity = this.getById(entity.getId());
        if (ObjectUtils.notNull(entity)) {
            // 删除 redis
            this.removeCache(entity);
            // 删除数据
            this.removeById(entity);
        }
    }

    @Override
    public AppClientEntity validToken(String appKey, String token, String timespan) {
        AppClientEntity entity = this.getByAppKey(appKey);
        try {
            if (ObjectUtils.isNull(entity)
                    || StringUtils.isAnyBlank(appKey, token, timespan)) {
                // 如果entity为空，直接返回无效的token
                throw new StriveInvalidTokenException();
            }
            // 验证时间
            long timeSeconds = Long.parseLong(timespan);
            long lowSeconds = LocalDateTime.now()
                    .minusMinutes(5).toEpochSecond(
                            OffsetDateTime.now().getOffset());
            long heightSeconds = LocalDateTime.now()
                    .plusMinutes(5).toEpochSecond(
                            OffsetDateTime.now().getOffset());
            if (lowSeconds > timeSeconds || heightSeconds < timeSeconds) {
                throw new StriveInvalidTokenException();
            }
            // 验证token
            String tokenStr = DigestUtils.md5Hex(
                    entity.getAppKey() + timespan + entity.getAppSecret());
            if (!StringUtils.equals(token, tokenStr)) {
                throw new StriveInvalidTokenException();
            }
        } catch (Exception e) {
            throw new StriveInvalidTokenException();
        }
        return entity;
    }

    @Override
    public AppClientEntity getByAppKey(String appKey) {
        AppClientEntity entity = RedisUtils.hashGet(appKey, appKey, AppClientEntity.class);
        if (ObjectUtils.isNull(entity)) {
            QueryWrapper<AppClientEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("app_key", appKey);
            entity = this.getOne(queryWrapper, false);
            if (ObjectUtils.isNull(entity)) {
                // 在内存创建一个默认的应用
                entity = new AppClientEntity();
                entity.setId(0L);
                entity.setAppKey("coconut_app_client_uyhloh")
                        .setAppSecret("coconut_app_secret_qfceta")
                        .setPath("/cauth");
            }
            this.updateCache(entity);
            return entity;
        }
        return entity;
    }

    @Override
    public StriveAuthInfo getAuthByAppKey(String appKey) {
        AppClientEntity entity = this.getByAppKey(appKey);
        if (ObjectUtils.notNull(entity)) {
            StriveAuthInfo authInfo = new StriveAuthInfo();
            authInfo.setLoginName(entity.getAppKey())
                    .setPassword(entity.getAppSecret());
            RbacUserEntity user = new RbacUserEntity();
            user.setId(entity.getId());
            user.setLoginName(entity.getAppKey())
                    .setPassword(entity.getAppSecret());
            authInfo.setUser(user);
            return authInfo;
        }
        return null;
    }

    @Override
    public void updateCache(AppClientEntity entity) {
        if (ObjectUtils.notNull(entity)) {
            RedisUtils.hashPut(appKey, entity.getAppKey(), entity);
        }
    }

    @Override
    public void removeCache(AppClientEntity entity) {
        if (ObjectUtils.notNull(entity)) {
            RedisUtils.hashDelete(appKey, entity.getAppKey());
        }
    }
}
